#!/usr/bin/env bash
# file: common_h
#
# Copyright (C) 2015 Ubuntu Kylin
#
# Author: Min Chen <minchen@ubuntukylin.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Library Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library Public License for more details.
#

my_dir=$(dirname "$0")

# admin node init path
rbd_image=/var/rbd_tool/rbd_image
database=$rbd_image/database
image_coll_v1=$rbd_image/image_coll_v1
image_coll_v2=$rbd_image/image_coll_v2
pg_coll=$rbd_image/pg_coll
images=$rbd_image/images
images_meta=$rbd_image/images_meta
default_backup_dir=/var/rbd_tool/default_backup_dir

# admin node: image snap & nosnap
nosnap= #$rbd_image/<image_name>/nosnap
snap= #rbd_image/<image_name>/<snap_name>

# osd node init path
job_path=/var/rbd_tool/osd_job
single_node=/var/rbd_tool/single_node

# osd node vars
osd_env= #single_node/$cluster$id/osd_env
osd_data= #/var/lib/ceph/osd/$cluster-$id
omap_path= #$osd_data/current/omap
image_list_v1= #single_node/$cluster-$id/image_list_v1
image_list_v2= #single_node/$cluster-$id/image_list_v2
image_v1= #$single_node/$cluster-$id/image_v1
image_v2= #$single_node/$cluster-$id/image_v2
pgid_list= #$single_node/$cluster-$id/pgid_list
node_pg_epoch= #$single_node/$cluster-$id/node_pg_epoch
omap_list= #$single_node/$cluster-$id/omap_list 

# admin node config file
osd_host_path=$my_dir/config/osd_host_path
osd_host_mapping= #$pwd_path/config/osd_host_mapping # host --> host_remote: by init_env_admin()
osd_host=$my_dir/config/osd_host #generated by function init_env_admin()
mon_host=$my_dir/config/mon_host
mds_host=$my_dir/config/mds_host

# ssh option
ssh_option="-o ConnectTimeout=1"

# gen md5sum
function gen_md5()
{
  echo $1|md5sum|awk '{print $1}'
}

# on each osd node
# check ceph environment: ssh, ceph-kvstore-tool, osd_data_path 
function check_ceph_env()
{
  local func="check_ceph_env"
  if [ $# -lt 2 ];then
    echo "$func: parameters: <node> <data_path>" 
    exit
  fi
  local node=$1
  local data_path=$2
  local res=
  local cmd=

  trap 'echo [$node]: ssh failed; exit' INT HUP
  ssh -o ConnectTimeout=1 $node "echo -n" </dev/null
  res=$?
  if [ $res -ne 0 ];then
    echo "[$node]: ssh failed"
    exit
  fi

  cmd=ceph-kvstore-tool
  trap 'echo [$node]: $cmd failed; exit' INT HUP
  ssh -o ConnectTimeout=1 $node "$cmd &>/dev/null;" </dev/null 
  res=$?
  # ceph-kvstore-tool will return 1 with no parameters input
  if [ $res -ne 1 ];then
    echo "[$node]: $cmd not installed"
    exit
  fi

  trap 'echo [$node]: stat $data_path failed; exit' INT HUP
  ssh -o ConnectTimeout=1 $node "stat $data_path &>/dev/null;"  </dev/null
  res=$?
  if [ $res -ne 0 ];then
    echo "[$node]: $data_path not exists"
    exit
  fi
}

# osd node context : osd_data_path
function init_env_osd()
{
  local func="init_env_osd"
  if [ "$1"x = ""x ];then
    echo "$func: no osd_data_path input" 
    exit
  fi
  osd_data=$1
  omap_path=$osd_data/current/omap

  if [ ! -e $single_node ];then
    mkdir -p $single_node
  fi

  local osd_id=`gen_md5 $osd_data`
  local osd_dir=$single_node/$osd_id

  if [ ! -e $osd_dir ];then
    mkdir -p $osd_dir
  fi
 
  image_list_v1=$osd_dir/image_list_v1
  image_list_v2=$osd_dir/image_list_v2
  image_v1=$osd_dir/image_v1
  image_v2=$osd_dir/image_v2
  pgid_list=$osd_dir/pgid_list
  node_pg_epoch=$osd_dir/node_pg_epoch
  omap_list=$osd_dir/omap_list
}

# admin node process file: osd_host_path
function init_env_admin()
{
  local func="init_env_admin" 
  local pwd_path=`pwd`
  osd_host_mapping=$pwd_path/config/osd_host_mapping
  if [ ! -s $osd_host_path ];then
    echo "$func: config/osd_host_path not exists or empty"
    exit
  fi
  if [ ! -e $rbd_image ];then
    mkdir -p $rbd_image
  fi
  if [ ! -e $images ];then
    mkdir -p $images
  fi

  if [ ! -s $mon_host ];then
    echo "$func: config/mon_host not exists or empty"
    exit
  fi
  if [ ! -e $mds_host ];then
    echo "$func: config/mds_host not exists"
    exit
  fi

  # we just judge if osd_host is needed to be updated
  if [ -s $osd_host ] && [ $osd_host -nt $osd_host_path ];then
    return  
  fi
  echo "$func: create osd_host ..."
  # create file: osd_host and osd_host_mapping
  >$osd_host
  >$osd_host_mapping
  local lines=0
  local lineno=0
  while read line
  do
    lineno=$(($lineno + 1))
    if [ "$line"x = ""x ];then
      continue;
    fi
    local node=`echo $line|awk '{print $1}'`
    if [ "$node"x = ""x ];then
      echo "$func: osd_host_path : line $lineno: osd hostname not input"
      rm -rf $osd_host $osd_host_mapping
      exit
    fi
    local data_path=`echo $line|awk '{print $2}'`
    if [ "$data_path"x = ""x ];then
      echo "$func: osd_host_path : line $lineno: osd data_path not input"
      rm -rf $osd_host $osd_host_mapping
      exit
    fi
    lines=$(($lines + 1))
    # in case : there are servral hostnames on the same node
    # just need output of `hostname`
    local hostname_alias=
    hostname_alias=`ssh $ssh_option $node "hostname" 2>/dev/null </dev/null`
    if [ "$hostname_alias"x = ""x ];then
      echo "$func: osd_host_path: line $lineno: $node: get remote hostname alias failed"
      rm -rf $osd_host $osd_host_mapping
      exit
    fi
    echo "$node $hostname_alias" >>$osd_host_mapping
    echo $node >> $osd_host
    # check ceph env on remote osd
    check_ceph_env $node $data_path
  done < $osd_host_path

  if [ $lines = 0 ];then
    echo "$func: no osd host path valid"
    exit
  fi
}

function admin_parse_osd()
{
  local func="admin_parse_osd"
  if [ -s $osd_host ];then
    return  
  fi
  # create file: osd_host
  >$osd_host
  local lines=0
  local lineno=0
  while read line
  do
    lineno=$(($lineno + 1))
    if [ "$line"x = ""x ];then
      continue;
    fi
    local node=`echo $line|awk '{print $1}'`
    if [ "$node"x = ""x ];then
      echo "$func: osd_host_path : line $lineno: osd_host not input"
      exit
    fi
    local data_path=`echo $line|awk '{print $2}'`
    if [ "$data_path"x = ""x ];then
      echo "$func: osd_host_path : line $lineno: osd_data not input"
      exit
    fi
    lines=$(($lines + 1))
    echo $node >> $osd_host
  done < $osd_host_path
}

# for osd node
function get_omap_list()
{
  ceph-kvstore-tool $omap_path list > $omap_list
}

function convert_underline()
{
  if [ "$1"x = ""x ];then
    return
  fi

  echo $1|sed -e 's/_/\\u/gp'|head -n 1
}

function dump_backslash()
{
  echo $*|sed -e 's/\\/\\\\/gp'|head -n 1
}

function dump_dump_backslash()
{
  echo $*|sed -e 's/\\/\\\\\\\\/gp'|head -n 1
}

function char_convert()
{
  if [ "$1"x = ""x ];then
    return
  fi

  echo $1|sed -e 's/_/\\u/gp' -e 's/\./%e/gp' -e 's/%/%p/gp'|head -n 1
}

function check_osd_process()
{
  local func="check_osd_process"
  local host=$1
  if [ "$1"x = ""x ];then
    exit
  fi
  local cmds="ps aux|grep ceph-osd|grep -v grep"
  local ret=/tmp/ret.$$$$
  ssh $ssh_option $host $cmds |tee $ret
  if [ -s $ret ];then
    echo "$func: [$host] ceph-osd process is not killed"
    exit
  fi
  rm -f $ret 
}

function get_map_header_prefix()
{
  echo "_HOBJTOSEQ_"
}

function get_map_header_key()
{
  local func="get_map_header_key"
  if [ "$1"x = ""x ];then
    #echo $func': no keyword input'
    exit 
  fi 
  local keyword=$1
  local res=`cat $omap_list| grep $keyword`
  if [ "$res"x = ""x ];then
    #echo "$func: map_header_key = $keyword not exists"
    exit
  fi
  echo $res|awk -F ":" '{print $2}'
}

function get_header_seq() 
{
  local func="get_header_seq"
  if [ "$1"x == ""x ];then
    #echo "$func: no prefix input"
    exit;
  elif [ "$2"x == ""x ];then
    #echo "$func: no key input"
    exit;
  fi
  local prefix=$1;
  local key=$2;
  local res=/tmp/header_seq.$$$$

  ceph-kvstore-tool $omap_path get $prefix $key 2>/dev/null 1>$res
  if [ $? != 0 ]; then
    #echo "$func: <$prefix , $key> not exists" ;
    exit;
  fi

  # ceph-kvstore-tool get result like this:
  # 02 01 7e 00 00 00 12 44 00 00 00 00 00 00 00 00
  # get header seq bytes: 
  # 12 44 00 00 00 00 00 00 
  # -> 00 00 00 00 00 00 44 12 
  # echo $((16#0000000000004412)) -> 17426 == header_seq
  local seq=`cat $res |head -n 2|tail -n 1| \
  awk '
  BEGIN {
    FS=":"
    seq="";
    i=7;
  } {
    split($2, arr, " ")  
    # header_seq uint64 : 8 bytes
    for (x=7; x>=0; --x) {
      seq=seq""arr[i+x];
   }
  }
  END {
   print seq
  }'`
  if [ "$seq"x = ""x ];then
    #echo "$func: get <$prefix , $key> failed"
    exit;
  fi
  rm -f $res
  echo $((16#$seq))
}

# get header info key/value
function get_header_kv()
{
  local func="get_header_kv"
  if [ "$1"x = ""x ];then
    #echo "$func: no prefix input"
    exit
  elif [ "$2"x = ""x ];then
    #echo "$func: no key input"
    exit
  elif [ "$3"x != "string"x ] && [ "$3"x != "int"x ];then
    #echo "$func: no valid type input, use type (string|int)"
    exit
  fi

  local prefix=$1
  local key=$2
  local types=$3
  local res=/tmp/kv.$$$$

  ceph-kvstore-tool $omap_path get $prefix $key 2>/dev/null 1>$res
  if [ $? != 0 ];then
    #echo "$func: <$prefix , $key> not exists" 
    exit
  fi

  if [ "$types"x = "string"x ];then
    local value=`cat $res |tail -n +2|head -n -1|awk -F ": " '{printf $3}'|sed -n 's/^\.\{4\}//p'`
    echo $value
  elif [ "$types"x = "int"x ];then
    local value=`cat $res |tail -n +2|head -n -1| \
      awk '
        BEGIN{
          FS=":"
        } {
          split($2, arr, " ");
          len=length(arr)
          for (i=len; i>0; --i) { 
                printf arr[i];
          }
        }'`
    echo $((16#$value))
  fi
  rm -f $res
}
